// TestClient
// 
// Application for stress-testing of a TCP or UDP server
// 
// Author: F.M.A. Erich
// HAN University of Applied Science
// Arnhem, The Netherlands


using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using Mono.Unix.Native;
using System.Runtime.CompilerServices;

namespace TestClient
{
	public interface IClient
	{
		void transmitPackets();
	}
	
	class MainClass
	{
		int activeThreads = 0;
		
		public static void Main(string[] args)
		{
			if (args.Length != 6) 
			{
				Console.WriteLine("This application requires the following parameters: <ip-address> <port> <UDP|TCP> <clientcount> <requestcount> <bytecount>");
				return;
			}
			
			String serverAddr = args[0];
			int port = Convert.ToInt32(args[1]);
			string protocol = "UDP";
			if (args[2].Equals("TCP")) protocol = "TCP";
			int clientCount = Convert.ToInt32(args[3]);
			int requestCount = Convert.ToInt32(args[4]);
			int byteCount = Convert.ToInt32(args[5]);
			
			MainClass mc = new MainClass(serverAddr, port, protocol, clientCount, requestCount, byteCount);
		}
		
		public MainClass(String serverAddr, int port, string protocol, int clientCount, int requestCount, int byteCount)
		{
			string[] serverAddrSplitted = serverAddr.Split('.');
			byte[] serverAddrBytes = new byte[4];
			for (int i=0;i<4;i++) serverAddrBytes[i] = Convert.ToByte(serverAddrSplitted[i]);
			
			IClient c;
			DateTime dtStart = DateTime.Now;
			for (int i=0;i<clientCount;i++)
			{
				if (protocol.Equals("TCP")) c = new Client(this, serverAddrBytes, port, requestCount, byteCount);
				else c = new Client2(this, serverAddr, port, requestCount, byteCount);
				
				ThreadStart job = new ThreadStart(c.transmitPackets);
				Thread t = new Thread(job);
				t.Start();
				activeThreads++;
			}
			while(activeThreads > 0);
			DateTime dtEnd = DateTime.Now;
			TimeSpan elapsedTime = dtEnd.Subtract(dtStart);
			Console.WriteLine("Finished query with {0} clients connecting to a {1} server, sending {2} requests composed of {3} bytes. Time elapsed: {4}", clientCount, protocol, requestCount, byteCount, elapsedTime.ToString());
		}
		
		[MethodImplAttribute(MethodImplOptions.Synchronized)] public void callBack()
		{
			activeThreads--;
			Console.WriteLine("Active threads: {0}", activeThreads);
		}
	}
	
	class Client : IClient
	{
		TcpClient client;
		int requestCount=0;
		int byteCount=0;
		private MainClass mc;
		
		public Client(MainClass mc, byte[] serverAddrBytes, int port, int requestCount, int byteCount)
		{
			client = new TcpClient();
			IPAddress ipa = new IPAddress(serverAddrBytes);
			client.Connect(ipa, port);
			this.requestCount = requestCount;
			this.byteCount = byteCount;
			this.mc = mc;
		}
		
		public void transmitPackets()
		{
			NetworkStream ns = client.GetStream();			
			byte[] byteBuffer = new byte[byteCount];
	
			for (int i=0;i<requestCount;i++)
			{
				ns.Write(byteBuffer, 0, byteCount);
				ns.Read(byteBuffer, 0, byteCount);
			}
			
			client.Close();
			mc.callBack();
		}
	}
	
	class Client2 : IClient
	{
		UdpClient client;
		int requestCount=0;
		int byteCount=0;
		int port;
		string server;
		IPEndPoint iep = new IPEndPoint(IPAddress.Any, 0);
		private MainClass mc;
		
		public Client2(MainClass mc, string server, int port, int requestCount, int byteCount)
		{
			client = new UdpClient();
			this.requestCount = requestCount;
			this.byteCount = byteCount;
			this.server = server;
			this.port = port;
			this.mc = mc;
		}
		
		public void transmitPackets()
		{
			byte[] byteBuffer = new byte[byteCount];
			
			
			for (int i=0;i<requestCount;i++)
			{
				client.Send(byteBuffer, byteCount, server, port);
				client.Receive(ref iep);
			}
			
			mc.callBack();
		}
	}
}